﻿using System;
using System.IO;
using System.Text;
using System.Windows;
using Microsoft.Win32;

namespace EffectEd
{
    public class DocumentController
    {

        private const string FileDialogFilter = "Effect files (*.fx;*.hlsl)|*.fx;*.hlsl|All files|*.*";

        private string _currentFileName;
        private FileSystemWatcher _watcher;

        /// <summary>
        /// Checks if modifications exist and saves them if user agrees.
        /// </summary>
        /// <returns>true = modifications are saved or discarded by user, false = discarding changes should be canceled</returns>
        public bool CheckAndSaveModifications()
        {
            if (_modified)
            {
                var result = MessageBox.Show("Current document has been modified. Save it first?", "Save first",
                                             MessageBoxButton.YesNoCancel);
                if (result == MessageBoxResult.Yes)
                {
                    return Save(); // replacement if save is successful
                }
                if (result == MessageBoxResult.Cancel)
                {
                    return false; // user wants no replacement
                }
            }
            return true; // no changes -> replacement is OK
        }

        public bool Save()
        {
            if (FileName == null)
            {
                return SaveAs();
            }
            _watcher.EnableRaisingEvents = false;
            File.WriteAllText(FileName, GetContent(), Encoding.ASCII);
            _watcher.EnableRaisingEvents = true;
            Modified = false;
            return true;
        }

        private string GetContent()
        {
            var e = new ContentEventArgs();
            if (ContentRequested != null) ContentRequested(this, e);
            return e.Content;
        }

        public bool SaveAs()
        {
            var dialog = new SaveFileDialog
            {
                FileName = _currentFileName,
                DefaultExt = "fx",
                Title = "Save effect file",
                Filter = FileDialogFilter
            };
            if (dialog.ShowDialog().Value)
            {
                FileName = dialog.FileName;
                return Save();
            }
            return false;
        }

        public void New()
        {
            string filename;
            if (ChooseTemplate(out filename) && CheckAndSaveModifications())
            {
                MakeNew(filename);
            }
        }

        private bool ChooseTemplate(out string filename)
        {
            var chooseTemplateForm = new ChooseTemplateWindow();
            var result = chooseTemplateForm.ShowDialog();
            if (result.HasValue && result.Value)
            {
                filename = chooseTemplateForm.SelectedFilename;
                return true;
            }
            filename = null;
            return false;
        }

        private void MakeNew(string templateFilename)
        {
            if (ContentSet != null) ContentSet(this, new ContentEventArgs() { Content = File.ReadAllText(templateFilename) });
            FileName = null;
        }

        public void Load()
        {
            if (!CheckAndSaveModifications()) return;
            var dialog = new OpenFileDialog { Filter = FileDialogFilter, Title = "Load effect file", DefaultExt = "fx" };
            if (dialog.ShowDialog().Value)
            {
                LoadUnsafe(dialog.FileName);
            }
        }

        public void Load(string filename)
        {
            if (!CheckAndSaveModifications()) return;
            LoadUnsafe(filename);
        }

        private void LoadUnsafe(string fileName)
        {
            FileName = fileName;
            if (ContentSet != null) ContentSet(this, new ContentEventArgs() { Content = File.ReadAllText(fileName) });
            Modified = false;
        }

        public void Reload()
        {
            Load(FileName);
        }

        private void WatcherOnChanged(object sender, FileSystemEventArgs fileSystemEventArgs)
        {
            FileHasChangedOutOfProc = true;
        }

        public string FileName
        {
            get { return _currentFileName; }
            set
            {
                _currentFileName = value;
                if (value != null)
                {
                    _watcher = new FileSystemWatcher(Path.GetDirectoryName(value), Path.GetFileName(value)) { NotifyFilter = NotifyFilters.LastWrite };
                    _watcher.Changed += WatcherOnChanged;
                    _watcher.EnableRaisingEvents = true;
                }
                if (FileNameChanged != null) FileNameChanged(this, EventArgs.Empty);
            }
        }

        private bool _modified;
        /// <summary>
        /// Whether the content of the document has changed.
        /// </summary>
        public bool Modified
        {
            get { return _modified; }
            set
            {
                _modified = value;
                if (ModifiedChanged != null)
                    ModifiedChanged(this, EventArgs.Empty);
            }
        }

        public bool FileHasChangedOutOfProc { get; set; }

        public EventHandler FileNameChanged;
        public EventHandler<ContentEventArgs> ContentSet;
        public EventHandler ModifiedChanged;
        public EventHandler<ContentEventArgs> ContentRequested;

    }
}